package com.comunicacion2.heatmap;

import java.awt.Image;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.Set;

public class IntensityHeatMap {

	private static final String DEFAULT_TITLE = "Heat Map";
	private static final String X_AXIS_LABEL = "Distancia en X (mts)";
	private static final String Y_AXIS_LABEL = "Distancia en Y (mts)";

	private final HeatChart heatChart;

	private Integer lowestX;
	private Integer highestX;

	private Integer lowestY;
	private Integer highestY;

	public IntensityHeatMap(Map<Location, Integer> intensityMap, String title) {

		if (intensityMap == null || intensityMap.isEmpty()) {
			throw new IllegalArgumentException("IntensityMap cannot be null or empty");
		}

		double[][] data = getMatrixFromIntensityMap(intensityMap);
		heatChart = new HeatChart(data);

		if (title != null) {
			heatChart.setTitle(title);
		}
		heatChart.setXAxisLabel(X_AXIS_LABEL);
		heatChart.setYAxisLabel(Y_AXIS_LABEL);

		heatChart.setShowXAxisValues(true);
		heatChart.setShowYAxisValues(true);

		heatChart.setXValues(lowestX, 1);
		Integer[] yValues = getYValues();
		heatChart.setYValues(yValues);
	}

	public IntensityHeatMap(Map<Location, Integer> intensityMap) {
		this(intensityMap, DEFAULT_TITLE);
	}

	public void saveToFile(String fileName) {

		try {
			heatChart.saveToFile(new File(fileName));
		} catch (IOException e) {

			throw new IllegalStateException(String.format("Could not save heat map to file '%s'", fileName), e);
		}
	}

	public Image getChartImage() {
		return heatChart.getChartImage();
	}

	private double[][] getMatrixFromIntensityMap(Map<Location, Integer> intensityMap) {

		Set<Location> keySet = intensityMap.keySet();
		calculateLocationRanges(keySet);

		int rangeX = highestX - lowestX;
		int rangeY = highestY - lowestY;

		double[][] data = new double[rangeY + 1][rangeX + 1];
		for (int i = 0; i < data.length; i++) {

			for (int j = 0; j <= rangeX; j++) {
				Location location = new Location(lowestX + j, lowestY + i);
				Integer value = intensityMap.get(location);
				if (value == null) {
					value = 0;
				}
				data[rangeY - i][j] = value;
			}
		}

		return data;
	}

	private void calculateLocationRanges(Set<Location> locationsSet) {

		Location[] locations = locationsSet.toArray(new Location[] {});

		lowestX = locations[0].getX();
		highestX = locations[locations.length - 1].getX();

		for (Location location : locations) {
			int y = location.getY();
			if (lowestY == null || y < lowestY) {
				lowestY = y;
			}
			if (highestY == null || y > highestY) {
				highestY = y;
			}
		}
	}

	private Integer[] getYValues() {

		int rangeY = highestY - lowestY;
		Integer[] yValues = new Integer[rangeY + 1];

		for (int i = 0; i <= rangeY; i++) {
			yValues[i] = highestY - i;
		}
		return yValues;
	}

}
